//
// Created by cljh on 2023/3/28.
//

#ifndef BIN_PACKING_NEST_H
#define BIN_PACKING_NEST_H


#include <vector>
#include <map>
#include <iostream>
#include "../entity/NestPath.h"
#include "../config/NestConfig.h"
#include "GeneticAlgorithm.h"
#include "../entity/NfpConfig.h"
#include "../entity/NfpKey.h"
#include "../utils/NfpUtils.h"
#include "../entity/PolygonList.h"
#include "Placement.h"
#include <QMetaType>
#include <QObject>
#include "QDebug"
#include<QMutex>
#include "SimulatedAnnealing.h"

class Nest : public QObject{
    Q_OBJECT
public:
    NestPath bin;
    std::vector<NestPath> parts;
    NestConfig config;
    GeneticAlgorithm *GA;
    SimulatedAnnealing *SA;
    std::map<std::string,NestPath>* nfpCache;
    double total_area; //裁片总面积
    double bin_height;

    Individual bestIndividual;

    Nest(NestPath _bin, std::vector<NestPath> _parts, NestConfig _config, QObject *parent = nullptr): QObject(parent) {
        bin = _bin;
        parts = _parts;
        config = _config;
        nfpCache =new std::map<std::string,NestPath>();
        GA=nullptr;
        SA = nullptr;
        bestIndividual=Individual();
        m_isStopNest = true;
        bin_height = GeometryUtils::getBoundingBoxSize(bin)[1];
        total_area = 0.0;
    }
    ~Nest(){
        qDebug() << "Nest ThreadObject destroy";
//        for(auto &pair : *nfpCache){
//            pair.second.selfDelete();
//        }
//        delete nfpCache;
        if(GA){
//            delete GA->population;
            delete GA;
        }
        if(SA){
            delete SA;
        }


    }
    void printNestAns(){
        std::cout<<"best fitness is: "<<bestIndividual.fitness<<std::endl;
        for(int i=0;i<bestIndividual.size();i++){
            NestPath path=bestIndividual.getPath(i);
            Vector offset=bestIndividual.placement[i];

        }
    }
    void trimNest(){
        for(size_t i=0;i<parts.size();i++){
            NestPath *nestPath=&parts[i];
            total_area += nestPath->area;
            for(int j=0;j<nestPath->size();j++){
                Polygon *polygon=(*nestPath)[j];
                Point reference=polygon->getPoint(0);
                for(int k=0;k<polygon->lenth;k++){
                    polygon->pointVector[k]=polygon->pointVector[k]-reference;
                }
            }
        }
    }
    Individual iterateGA(GeneticAlgorithm *ga){

        std::vector<Individual*> population=ga->population;

        Individual ret=Individual();
        std::cout<<"population size is: "<<population.size()<<std::endl;
        for(size_t k=0;k<population.size();k++){
            Individual *individual=population.at(k);
            if(individual->fitness==DBL_MIN){
                std::vector<NfpConfig> nfpVector=std::vector<NfpConfig>();
                //再看是否在nfpCache中
                for(int i=0;i<individual->size();i++){
                    NestPath path=individual->getPath(i);
                    NfpKey keyBin=NfpKey(0,path.id, true,0,path.rotation);
                    NfpConfig nfpConfig=NfpConfig(bin,path,keyBin);
                    nfpVector.push_back(nfpConfig);
                    for(int j=0;j<i;j++){
                        NestPath placed=individual->getPath(j);
                        NfpKey keyPlaced=NfpKey(placed.id,path.id, false,placed.rotation,path.rotation);
                        NfpConfig nfpConfig=NfpConfig(placed,path,keyPlaced);
                        nfpVector.push_back(nfpConfig);
                    }
                }
                for(NfpConfig &nfpConfig:nfpVector){
                    NfpKey key=nfpConfig.key;
                    std::string nfpKey=key.toString();
                    if(nfpCache->count(nfpKey)!=1){

                        NestPath nfp=NfpUtils::nfpGenerator(nfpConfig);
                        if(nfp.size()==0){
                            std::cout<<"nfp error : key message is "<<key.toString()<<std::endl;
                            continue;
                        }
                        std::pair<std::string,NestPath> nfpPair={key.toString(),nfp};
                        nfpCache->insert(nfpPair);
                        //                        std::cout<<"generate nfp: "<<nfpKey<<std::endl;
                    }
                }
                //再摆放
                PolygonList polygonList=PolygonList(individual->paths);

                Placement placement=Placement(bin,nfpCache);

                std::vector<Vector> retPlacements=std::vector<Vector>();
                double retFitness=0;
                double rectWidth = 0;
                if(placement.placePolygons(polygonList,retPlacements,retFitness, rectWidth)){
                    individual->fitness=retFitness;
                    individual->placement=retPlacements;
                    individual->utilization = total_area/(rectWidth*bin_height);
                }
                //                population[k]=individual;
                //                std::cout<<"individual i: "<<k<<"'s fitness is: "<<individual.fitness<<" utilization_rate is: "<<individual.utilization_rate<<std::endl;
            }
            if(individual->fitness>ret.fitness){
                    ret.fitness=individual->fitness;
                    ret.placement=individual->placement;
                    ret.paths=individual->paths;
                    ret.utilization = individual->utilization;
                }
        }

        return ret;

    }

public slots:
    void stopNest(){
        QMutexLocker locker(&m_stopMutex);
        m_isStopNest = true;
    }

    void startGANest(){

        trimNest();
        GA= new GeneticAlgorithm(config.POPULATION_SIZE,config.MUTATION_RATE,config.ANGLE_SCALE);
        int maxLoop =config.MAX_LOOP;
        int loopCnt = 0;

        GA->initPopulation(parts);
        {
            QMutexLocker locker(&m_stopMutex);
            m_isStopNest = false;
        }
        for(int i=0;i<maxLoop;i++){
            {
                QMutexLocker locker(&m_stopMutex);
                if(m_isStopNest){
                    emit nestDone();
                    return;
                }
            }
            //循环迭代，每次迭代返回一个最好的利用率和最好的摆放
//            std::cout<<"no : "<<i<<" iterate"<<std::endl;
            Individual bestForNow=iterateGA(GA);
            if(bestForNow.fitness>bestIndividual.fitness){
                bestIndividual=bestForNow;
                emit drawTemporaryOutput(this->bestIndividual);
            }

            GA->generation();
            loopCnt++;
            emit updateNestState(this->bestIndividual.utilization, loopCnt, 0);
        }

        emit nestDone();
    };
    void startSANest(){
        trimNest();
        qDebug()<<"startSA";
        SA = new SimulatedAnnealing(parts.size(), config.ANGLE_SCALE,config.T_0 ,config.ALPHA, config.END_TEMP, config.MAX_OUTER_ITERATION,config.MAX_INNER_ITERATION);
        {
            QMutexLocker locker(&m_stopMutex);
            m_isStopNest = false;
        }

        std::sort(parts.begin(),parts.end(),GeneticAlgorithm::cmp);
        Individual state_previous(parts);
        bestIndividual = state_previous;
        int nOuter = 0; // 降温计数
        double currentTemp = SA->t0; // 当前温度
        double deltaE = 0.0; // 目标函数差值
        double p = 0.0; // 选择新状态的概率
        while(nOuter < SA->maxOuterIteration && currentTemp > SA->endTemp){
            int nInner = 0; // 当前温度下的抽样计数
            {
                QMutexLocker locker(&m_stopMutex);
                if(m_isStopNest){
                    emit nestDone();
                    return;
                }
            }
            while(nInner < SA->maxInnerIteration){
                Individual state_new;
                if (nOuter == 0&& nInner==0){  // 最开始先计算初始排序
                    state_new = state_previous;
                }else{
                    state_new = SA->createNewState(state_previous); // 产生新状态
                }

                // 计算新状态的目标函数值(存入fitness)
                std::vector<NfpConfig> nfpVector=std::vector<NfpConfig>();
                //再看是否在nfpCache中
                for(int i=0;i<state_new.size();i++){
                    NestPath path=state_new.getPath(i);
                    NfpKey keyBin=NfpKey(0,path.id, true,0,path.rotation);
                    NfpConfig nfpConfig=NfpConfig(bin,path,keyBin);
                    nfpVector.push_back(nfpConfig);
                    for(int j=0;j<i;j++){
                        NestPath placed=state_new.getPath(j);
                        NfpKey keyPlaced=NfpKey(placed.id,path.id, false,placed.rotation,path.rotation);
                        nfpConfig=NfpConfig(placed,path,keyPlaced);
                        nfpVector.push_back(nfpConfig);
                    }
                }
                for(NfpConfig &nfpConfig:nfpVector){
                    NfpKey key=nfpConfig.key;
                    std::string nfpKey=key.toString();
                    if(nfpCache->count(nfpKey)!=1){

                        NestPath nfp=NfpUtils::nfpGenerator(nfpConfig);
                        if(nfp.size()==0){
                            std::cout<<"nfp error : key message is "<<key.toString()<<std::endl;
                            continue;
                        }
                        std::pair<std::string,NestPath> nfpPair={key.toString(),nfp};
                        nfpCache->insert(nfpPair);
                        //                        std::cout<<"generate nfp: "<<nfpKey<<std::endl;
                    }
                }
                //再摆放
                PolygonList polygonList=PolygonList(state_new.paths);

                Placement placement=Placement(bin,nfpCache);

                std::vector<Vector> retPlacements=std::vector<Vector>();
                double retFitness=0;
                double rectWidth = 0;
                try{
                    if(placement.placePolygons(polygonList,retPlacements,retFitness,rectWidth)){
                        state_new.fitness=retFitness;
                        state_new.placement=retPlacements;
                        state_new.utilization = total_area/ (rectWidth*bin_height);
                    }
                }catch(Clipper2Lib::Clipper2Exception e){
                    std::cout<<e.what();
                }
                std::cout<<"nOuter : "<<nOuter<<" nInner : "<<nInner<<"'s fitness is: "<<state_new.fitness<<std::endl;

                deltaE = state_new.fitness - state_previous.fitness; // 计算目标函数差值
                if(deltaE > 0){ // 如果新状态更优，则直接接受
                    state_previous = state_new;
                    if(state_new.fitness > bestIndividual.fitness){
                        bestIndividual = state_new; // 更新最优解
                        qDebug()<<"更新最优解"<<state_new.fitness;
                        emit drawTemporaryOutput(this->bestIndividual);  // 画出最优解
                        emit updateNestState(this->bestIndividual.utilization, nOuter, currentTemp);
                    }
                }else{ // 如果新状态不如当前状态，则按一定概率接受
                    p = exp(-deltaE / currentTemp);
                    if(SA->rand_double(SA->rng_) < p){
                        state_previous = state_new;
                    }
                }
                nInner++;
            }
            currentTemp = currentTemp * SA->alpha; // 计算下一个温度
            nOuter++;
            emit updateNestState(this->bestIndividual.utilization, nOuter, currentTemp);
        }
        emit nestDone();
    }


signals:
    void drawTemporaryOutput(Individual& individual);  // （是否是中间结果，当前最好的个体）
    void updateNestState(double uitilization, int out_count, double currentTemp);
    void nestDone();
private:
    bool m_isStopNest;

    QMutex m_stopMutex;
};

#endif //BIN_PACKING_NEST_H
